﻿using System;
using System.Linq;
using System.Web;
using System.Data;
using System.Web.Mvc;
using Web.Core.Extensions;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Web.Script.Serialization;

namespace Web.Core.HttpModules
{
    public class ErrorModule : IHttpModule
    {

        public void Init(HttpApplication application)
        {
            application.Error += ApplicationError;
        }

        public void Dispose()
        {

        }

        public void ApplicationError(object sender, System.EventArgs e)
        {
            var context = HttpContext.Current;
            Exception exception;
            for (exception = context.Server.GetLastError(); exception.InnerException != null; exception = exception.InnerException) { }
            //if (context.Request.IsLocal) return;
            context.ClearError();
            GetErrorLog(context, exception).Log();

            if (context.Request.RequestContext.HttpContext.Request.IsAjaxRequest())
            {
                var message = exception.Message;
                if (exception is HttpRequestValidationException)
                {
                    //从客户端(memo="<div>asdfasdf</div>")中检测到有潜在危险的 Request.Form 值。
                    message = "从客户端中检测到有潜在危险的Request.Form值。";
                }
                if (!context.Request.IsLocal)
                {
                    message = "发生了错误，请联系客服中心！";
                }
                context.Response.Clear();
                if (context.Request.AcceptTypes != null && context.Request.AcceptTypes.Contains("text/html"))
                {
                    context.Response.ContentType = "text/html";
                    context.Response.Write("<div>error</div>");
                }
                else
                {
                    context.Response.ContentType = "application/json";
                    var serializer = new JavaScriptSerializer();
                    var json = serializer.Serialize(new { Status = false, Message = message });
                    context.Response.Write(json);
                }
                context.Response.Flush();
                context.Response.End();
            }
            else
            {
                context.Response.Clear();
                context.Server.Transfer("~/error.htm", true);
            }
        }

        private static AppError GetErrorLog(HttpContext context, Exception exception)
        {
            var error = new AppError();
            var variables = context.Request.ServerVariables.AsDynamic(true);
            error.Machine = context.Server.MachineName;
            error.Server = variables.ServerName;
            error.ServerIp = variables.LocalAddr;
            error.ExType = exception.GetType().FullName;
            error.ExSource = exception.Source;
            error.ExMessage = exception.Message;
            error.ExStackTrace = exception.StackTrace;
            error.Url = variables.Url;
            error.UrlReferer = variables.Referer;
            error.UserAgent = variables.HttpUserAgent;
            error.IpAddress = variables.RemoteAddr;
            var nv = variables.Raw as NameValueCollection;
            if (nv != null)
            {
                var dic = new Dictionary<string, string>(nv.Count);
                foreach (var o in nv.AllKeys)
                {
                    dic.Add(o, nv[o]);
                }
                // error.Variables = dic.ToJson();
            }


            if (exception is HttpException)
            {
                var ex = exception as HttpException;
                error.CreatedBy = context.User.Identity.Name;
                error.IpAddress = context.Request.UserHostAddress;

                var statusCode = ex.GetHttpCode();
                error.StatusCode = statusCode;
                if (statusCode >= 500)
                {
                    error.Level = 2;
                }
                else if (statusCode >= 400 && statusCode < 500)
                {
                    error.Level = 1;
                }
                else
                {
                    error.Level = 0;
                }
            }
            return error;
        }
        internal sealed class AppError
        {
            /// <summary>
            /// 
            /// </summary>
            public string Machine { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string Server { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string ServerIp { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public byte Level { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string ExType { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string ExSource { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string ExMessage { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string ExStackTrace { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string Variables { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public int StatusCode { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string Url { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string UrlReferer { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string UserAgent { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string IpAddress { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string CreatedBy { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public DateTime CreatedOn { get; set; }

            public AppError()
            {
                Level = 3;
                StatusCode = 0;
            }
            public void Log()
            {
                using (var conn = new SqlConnection("server=.;database=AyattaExtra;uid=sa;pwd=123456;"))
                {
                    if (conn.State != ConnectionState.Open)
                    {
                        conn.Open();
                    }
                    const string procName = "[Proc.App.Exception.Add]";
                    using (var cmd = new SqlCommand(procName, conn) { CommandType = CommandType.StoredProcedure })
                    {
                        cmd.Parameters.AddWithValue("@Machine", Machine);
                        cmd.Parameters.AddWithValue("@Server", Server);
                        cmd.Parameters.AddWithValue("@ServerIp", ServerIp);
                        cmd.Parameters.AddWithValue("@Level", Level);
                        cmd.Parameters.AddWithValue("@ExType", ExType);
                        cmd.Parameters.AddWithValue("@ExSource", ExSource);
                        cmd.Parameters.AddWithValue("@ExMessage", ExMessage);
                        cmd.Parameters.AddWithValue("@ExStackTrace", ExStackTrace);
                        cmd.Parameters.AddWithValue("@Variables", Variables);
                        cmd.Parameters.AddWithValue("@StatusCode", StatusCode);
                        cmd.Parameters.AddWithValue("@Url", Url);
                        cmd.Parameters.AddWithValue("@UserAgent", UserAgent);
                        cmd.Parameters.AddWithValue("@UrlReferer", UrlReferer);
                        cmd.Parameters.AddWithValue("@IpAddress", IpAddress);
                        cmd.Parameters.AddWithValue("@CreatedBy", CreatedBy);
                        cmd.Parameters.AddWithValue("@CreatedOn", DateTime.Now);

                        cmd.ExecuteNonQuery();
                    }
                }
            }
        }
    }
}


